/*
* Copyright 2011 Andy Lee.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package uk.co.optimisticpanda.remoteweb.command.web;

import groovy.lang.Closure
import groovy.util.slurpersupport.GPathResult;

import org.openqa.selenium.By
import org.openqa.selenium.Keys
import org.openqa.selenium.WebDriver
import org.openqa.selenium.WebElement
import org.openqa.selenium.support.ui.WebDriverWait;
import com.google.common.base.Function

public enum WebActionType{
	select(this.&selectOption, ["id", "name", "value", "variable", "variableFinder"]),
	check(this.&toggleRadioOrCheckbox, ["id", "name"]),
	type(this.&typeTextIntoField, ["id", "name", "value", "variable", "variableFinder"]),
	go(this.&goTo, ["value"]),
	submit(this.&submit, ["id", "name"]),
	copy(this.&copy, ["id","name","fromId","fromName","value", "variable", "variableFinder"]),
	click(this.&click, ["id", "name"])

	Closure closure;
	List fields;

	private WebActionType(Closure closure, List validAttributes){
		this.closure = closure;
		this.fields = validAttributes;
	}

	/*null safe Enum.valueOf*/
	static WebActionType getType(result){
		if(result){
			return Enum.valueOf(WebActionType.class, String.valueOf(result))
		}else return null;
	}

	/*Get constructor args for web executable*/
	static def getWebExecutableArgs(GPathResult result){
		WebActionType type = getType(result.name())
		def attributes = result.attributes()
		def args = type.fields.inject([:]) { map, field ->
			if(attributes[field]) map[field] = String.valueOf( attributes[field])
			map;
		}
		[type :type, args : args ]
	}

	/*Get all attributes for this action plus description*/
	def getAttributes(description, args){
		this.fields.inject(["description":description]) { result, field ->
			result[field] = args[field]
			return result;
		}
	}
	
	/*Get the actions parameters in the defined order*/
	def getOrderedParams(args){
		this.fields.inject([]) { result, field ->
			result.add args[field]
			return result;
		}
	}

	@Override
	//TODO: refactor to just pass one map to one method and calculate type based on that.
	Closure getClosure(args){
		closure.curry(*this.getOrderedParams(args));
	}

	static void typeTextIntoField(String id, String name, String value, String variable, def variableFinder, WebDriver driver ){
		WebElement element = getElementByIdOrName(driver, id, name);
		element.sendKeys( getValue(variableFinder, variable, value));
	}

	static void copy(String id, String name, String fromId, String fromName, String value, String variable, def variableFinder, WebDriver driver ){
		WebElement element = getElementByIdOrName(driver, id, name);
		WebElement copyFromElement = getElementByIdOrName(driver, fromId, fromName);
		value = ["div","span"].contains(copyFromElement.tagName) ? copyFromElement.getText() : copyFromElement.getValue()
		element.sendKeys( getValue(variableFinder, variable, value) );
	}

	static void toggleRadioOrCheckbox(String id, String name, WebDriver driver){
		WebElement element = getElementByIdOrName(driver, id, name);
		element.toggle();
	}
	static void selectOption(String id, String name, String value, String variable, def variableFinder, WebDriver driver){
		WebElement element = getElementByIdOrName(driver, id, name);
		for (it in element.findElements(By.tagName("option"))){
			if(it.getValue() ==  getValue(variableFinder, variable, value)){
				it.setSelected()
				it.sendKeys Keys.TAB
				break;
			}
		}
	}
	static void click(String id, String name, WebDriver driver){
		getElementByIdOrName(driver, id, name).click();
	}

	static void submit(String id, String name, WebDriver driver){
		getElementByIdOrName(driver, id, name).submit();
	}
	static void goTo(String value, WebDriver driver){
		driver.get(value);
	}

	private static String getValue(variableFinder, variable, value){
		variable ? variableFinder.getValue(variable): value;
	}
	
	private static WebElement getElementByIdOrName(d, id, name){
		final def by = id ? By.id(id): By.name(name);

		WebDriverWait wait = new WebDriverWait(d, 3);
		wait.until(new Function() {
					public def apply(driver) {
						return driver.findElement(by)
					}
				});
	}
}
